package framework.aop;

import java.util.*;

/**
 * class that store the Aop configuration
 * that read from configuration file
 */
public class InchAopConfig {
    /** string that define the position of the aspect method that need to cut in for*/
    private static final String POINT_CUTS = "pointCuts";
    private static final String ASPECT_CLASS = "aspectClass";
    public static final String ASPECT_BEFORE = "aspectBefore";
    public static final String ASPECT_AFTER = "aspectAfter";
    public static final String ASPECT_AFTER_RETURNING = "aspectAfterReturning";
    public static final String ASPECT_AFTER_THROW = "aspectAfterThrowing";
    public static final String ASPECT_AROUND = "aspectAround";

    //all the possible position collection
    private static final String[] PROPERTIES_NAME_OF_ALL_POSSIBLE_ASPECT_METHOD
            = {ASPECT_BEFORE,ASPECT_AFTER,ASPECT_AFTER_RETURNING,ASPECT_AFTER_THROW,ASPECT_AROUND};

    private String[] pointCuts;                //point cuts method name
    private String aspectClass;             //name of the cut in aspect class

    /**
     * <pointcutName,<adviseName,aspectMethodName>> mapping
     *
     * this map indicate the aspect method(aspectMethodName) that want cut in
     * and where to cut in(adviseName) for this point cut
     */
    private Map<String,Map<String,String>> pointCutAspectMethodMapping;

    /**
     * Constructor with the config properties
     * because all the configuration is in one file,
     * aop config is not separated,so we just use the old reader
     * and get its properties to use
     *
     * @param config properties get from InchBeanDefinitionReader
     */
    public InchAopConfig(Properties config){
        //get properties
        pointCutAspectMethodMapping = new HashMap<String,Map<String, String>>();
        aspectClass = config.getProperty(ASPECT_CLASS);
        //pointcuts method is separated by ","
        pointCuts = config.getProperty(POINT_CUTS).split(",");
        for (String pointCut : pointCuts ) {
            Map<String,String> aspectMethods = new HashMap<String,String>();
            /**
             *  parse the string to find all aspect method assigned to this point cut
             *  eg:
             *
             *  expression in config file is like:
             *  [point cut method name];[advise name(cut in position/interceptor)]=[aspectName]
             *
             *  pointCutName: demo.services.impl.QueryService.query
             *
             *  aspectMethod assigned to it:
             *  demo.services.impl.QueryService.query;aspectBefore=demo.aspect.logAspect.before
             *
             */

            //get all possible key value for this point cut method in config file
            String[] names = getPropertiesNameOfAllPossibleAspectMethod(pointCut);
            //match to the config file to see which exist
            for(String name : names) {
                if(config.containsKey(name)){
                    /**parse string and get the advise name(cut in position/interceptor)
                     * aspectMethods structure is : [adviseName,aspectMethodName]
                     * eg: [aspectBefore,demo.aspect.logAspect.before]
                     */
                    aspectMethods.put(name.split(";")[1],config.getProperty(name));
                }
            }
            /**
             *  link aspectMethods with point cut
             * eg: [demo.services.impl.QueryService.query,[aspectBefore,demo.aspect.logAspect.before]]
             */
            pointCutAspectMethodMapping.put(pointCut,aspectMethods);
        }
    }

    /**
     * get all possible key value of the properties that store the aspect methods assigned for the point cut
     * @param pointCut the point cut you want to find out the aspect methods assigned to it
     * @return String[] of possible key value of the properties that store the aspect methods assigned for the point cut
     */
    private String[] getPropertiesNameOfAllPossibleAspectMethod(String pointCut) {
        String[] output = new String[PROPERTIES_NAME_OF_ALL_POSSIBLE_ASPECT_METHOD.length];
        for(int i = 0; i < PROPERTIES_NAME_OF_ALL_POSSIBLE_ASPECT_METHOD.length; i++){
            output[i] = pointCut + ";" + PROPERTIES_NAME_OF_ALL_POSSIBLE_ASPECT_METHOD[i];
        }
        return output;
    }

    public String[] getPointCuts() {
        return pointCuts;
    }

    public String getAspectClass() {
        return aspectClass;
    }
    public Map<String, Map<String, String>> getPointCutAspectMethodMapping() {
        return pointCutAspectMethodMapping;
    }
}
